package models;

import play.mvc.*;
import play.libs.*;
import scala.concurrent.Await;
import scala.concurrent.duration.Duration;
import akka.actor.*;
import static akka.pattern.Patterns.ask;

import com.fasterxml.jackson.databind.JsonNode;

import java.util.*;

import models.msgs.*;
import models.msgs.Call;
import models.msgs.Number;
import static java.util.concurrent.TimeUnit.*;

public class Table extends UntypedActor {
	static int gracze=0;
	static int boty=0;
	static int ludzie=0;
	static int kasa=1000;
	static int t;
	static int wpisowe=100;
	public static Game game;
	
	private int nr_zwyciezcy = 0;
	private static int stawka = 0;
	private static int pula = 0;
	//Flaga zapobiegająca wielokrotnemu tworzeniu klasy Game
	private static boolean pierwsze_logowanie=true;
	//Flaga zapobiegająca wielokrotnemu rozdaniu kart
	private boolean rozdane_karty=false,pierwsza_stawka=true,wymiana_kart=true,wylaniamy_winnera=false,all_in=false;
	//mozliwe ze nie bedzie potrzebne
	private int licznik_wejsc = 0;
	//zlicza czy kazdy wykonał swoj ruch
	private int obejscie_stołu = 0;
	//Ranking wolnych miejsc przy stole
	private static int[] wolne_miejsce = new int[4];

	
    // Default table.
    static ActorRef defaultTable = Akka.system().actorOf(Props.create(Table.class));
    // Members of this table.
    static Map<String, ActorRef> members = new HashMap<String, ActorRef>();
    
    
    private void GiveCards(int gracze)
    {
		 int i,j;
		 game.creator.deck.Shuffle();
		 for(j=0;j<5;j++){
			 for(i=0;i<gracze;i++){
				 game.creator.hand[i].cards[j]=game.creator.deck.GetCard();
			 }
		 }
		 for(i=0;i<gracze;i++){
			 game.creator.hand[i].sort();
		 }
	 }
       
    public static void join(final String name, WebSocket.In<JsonNode> in, WebSocket.Out<JsonNode> out, final String human, final String bot, final String money, final String wpisowe2) throws Exception
    {
    	int t;
        
    	if(pierwsze_logowanie){   		
    		ludzie=Integer.parseInt(human);
    		boty=Integer.parseInt(bot);
    		gracze=ludzie+boty;
    		kasa=Integer.parseInt(money);
    		wpisowe=Integer.parseInt(wpisowe2);
    		game = new Game(ludzie,boty,kasa);
    		pierwsze_logowanie=false;
    	}
    	
    	// Send the Join message to the table
        String result = (String)Await.result(ask(defaultTable,new Join(name), 1000), Duration.create(1, SECONDS));
              
        if("OK".equals(result)) {
        	
        for(t=0;t<4;t++)
        		if(wolne_miejsce[t]==0){
        			wolne_miejsce[t]=1;
        			break;
        		}
            ActorRef player =  Akka.system().actorOf(
                    Props.create(Human.class, name, in, out,  defaultTable , t));
            
            
            members.put(name, player);            
            String text =  "my Actor has been added to Table"; 
            notifyAll(new Info(text,name));         
            defaultTable.tell(new Cash(t,name,kasa,wpisowe),player);
        }
    }
    
    public static void botjoin(final String name) throws Exception
    {
    	
    	// Send the Join message to the table
        String result = (String)Await.result(ask(defaultTable,new Join(name), 1000), Duration.create(1, SECONDS));
              
        if("OK".equals(result)) {
        	 for(t=0;t<4;t++)
         		if(wolne_miejsce[t]==0){
         			wolne_miejsce[t]=1;
         			break;
         		}
            ActorRef player =  Akka.system().actorOf(
                    Props.create(Bot.class, name,  defaultTable , t));     
            
            members.put(name, player);
            
            notifyAll(new Info(name + " został utworzony ale czy działa ?",name));
        }
    }
       
    public void onReceive(Object message) throws Exception 
    {
        if(message instanceof Join) 
        {
            getSender().tell("OK", getSelf());             	
        } 
        else if (message instanceof Info)
        {   
        	String text = ((Info) message).getText();
        	String name = ((Info) message).getName();  
        	
        	notifyAll(new Info(text, name));
        }  
        else if(message instanceof Quit)  
        {            
           
            String name = ((Quit)message).getName(); 
            int nr = ((Quit)message).getNumber(); 
            wolne_miejsce[nr]=0;
            members.remove(name);
            String text = "has left the game";            		
            notifyAll(new Info(text, name));
            if(members.size()<=1)
           		notifyAll(new Info("Gratulacje wygrałeś!!!", "Table"));
          
        }
        else if(message instanceof Init)
        {
        	if(members.size() < gracze)
        	{
        		String text = "Prosze czekać na pozostałych graczy";   
	            getSender().tell(new Info(text, "Table"), getSelf());
        	}
        	else if (!rozdane_karty)
        	{
        		GiveCards(gracze);
        		rozdane_karty=true;
        		pierwsza_stawka=true;
        		wymiana_kart=true;
        		wylaniamy_winnera=false;
        		all_in=false;
        		game.clearFolds();
        		pula = 0;     		
        		obejscie_stołu=0;
        		stawka = wpisowe;
            	int nr = ((Init)message).getNumber();         	
        		game.takeBuyInCoins(wpisowe);
        		game.ustawPierwszaPuleGracza(wpisowe);
        		int kasa = 0, i = 0;
        		for (ActorRef member : members.values()) {
        			kasa = game.getCoins(i);
        			pula += wpisowe;
                    member.tell(new Cash(nr,"",kasa,stawka), defaultTable); 
                    i++;
        		}
        		notifyAll(new NewRound());
        		notifyAll(new Info("Rozdałem karty.","Table"));
        		notifyAll(new Init(licznik_wejsc,"Table"));
        		defaultTable.tell(new Rozdaj(nr), getSender());
        	}
        	else if (rozdane_karty)
        	{
        		int nr = ((Init)message).getNumber(); 
        		defaultTable.tell(new Rozdaj(nr), getSender());
        	}
        }
        else if(message instanceof Rozdaj)
        {
        	int nr = ((Rozdaj) message).getNumber();
    		String text = "Twoje karty to:";
    		getSender().tell(new Info(text, "Table"), getSelf());
           	for(int j=0; j<5; j++){
           		getSender().tell(new Card(game.getRankNr(nr,j), game.getColorNr(nr,j)), getSelf());
           	}
           	Kick();
        }
        else if(message instanceof Bet)
        {
        	int nr = ((Bet) message).getPlayerNumber();
        	if(nr!=licznik_wejsc || !pierwsza_stawka || !wymiana_kart || !rozdane_karty){
        		getSender().tell(new Info("Opcja zablokowana.", "Table"), getSelf()); 
        	}
        	else
        	{	 
        		int bet = ((Bet) message).getCash();
        		game.ustawPuleGracza(nr, bet);
            	game.takeCoins(bet,nr);        
            	int kasa = game.getCoins(nr);    	
            	if(kasa < 0 || bet <= 0)
            	{
            		game.giveCoins(bet, nr);
            		getSender().tell(new Info("Brak funduszy lub nie jestes dosc hojny", "Table"), getSelf()); 
            	}
            	else
            	{
            		String name = ((Bet)message).getName(); 		
	        		pierwsza_stawka = false;
	        		stawka = stawka + bet;
	        		pula = pula + bet;
	        		getSender().tell(new Cash(nr,name,kasa,wpisowe), defaultTable); 
	        		notifyAll(new Info("Gracz "+name+" podbija stawke o "+bet+", stawka wynosi : "+stawka, "Table"));
	        		licznik_wejsc++;
	        		obejscie_stołu++;
	        		notifyAll(new Init(licznik_wejsc,"Table"));
            	}
        	}
        }
        else if(message instanceof Raise)
        {
        	int nr = ((Raise) message).getPlayerNumber();
        	if(nr!=licznik_wejsc || pierwsza_stawka || !wymiana_kart  || !rozdane_karty)
        		getSender().tell(new Info("Opcja zablokowana! (Sprawdz czy jest twoja kolej lub czy została wstawiona pierwsza stawka)", "Table"), getSelf());  
        	else
        	{	        	
	        	int raise = ((Raise) message).getCash();
	        	int moja_pula = game.pobierzPuleGracza(nr);
            	game.takeCoins(raise+stawka-moja_pula,nr);   
            	game.ustawPuleGracza(nr, raise);	
            	int kasa = game.getCoins(nr);
            	if(kasa<=0 || raise <= 0)
            	{
            		game.giveCoins(raise+stawka-moja_pula, nr);
            		getSender().tell(new Info("Brak funduszy lub nie jestes dosc hojny", "Table"), getSelf()); 
            	}
            	else
            	{
            		obejscie_stołu=0;
            		String name = ((Raise)message).getName(); 	
	        		stawka = stawka + raise;
	        		pula = pula + raise;
	        		getSender().tell(new Cash(nr,name,kasa,wpisowe), defaultTable); 
	        		notifyAll(new Info("Gracz "+name+" podbija stawke o "+raise+", stawka wynosi : "+stawka, "Table"));
	        		
	        		next();    		
            	}
        	}
        }
        else if(message instanceof Check)
        {
        	int nr = ((Check) message).getPlayerNumber();
        	int moja_pula = game.pobierzPuleGracza(nr);
        	if(nr!=licznik_wejsc || moja_pula<stawka || !wymiana_kart || !rozdane_karty)
        		getSender().tell(new Info("Opcja zablokowana! (Sprawdz czy jest twoja kolej lub czy wyrównałeś rachunki)", "Table"), getSelf());
        	else
        	{
        		String name = ((Check)message).getName();
        		notifyAll(new Info("Gracz "+name+" czeka", "Table"));
        		pierwsza_stawka = false;   		
        		next();
        	}
        }
        else if(message instanceof Call)
        {
        	int nr = ((Call) message).getPlayerNumber();
        	int moja_pula = game.pobierzPuleGracza(nr);
        	if(nr!=licznik_wejsc || moja_pula>=stawka  || pierwsza_stawka || !rozdane_karty)
        		getSender().tell(new Info("Opcja zablokowana! (Sprawdz czy jest twoja kolej lub czy można coś dożucić do stawki)", "Table"), getSelf());
        	else
        	{
            	game.takeCoins(stawka-moja_pula,nr);   
            	game.ustawPuleGracza(nr, stawka-wpisowe);
            	int kasa = game.getCoins(nr);    	
            	if(kasa <= 0)
            	{
            		game.giveCoins(stawka-moja_pula, nr);
            		getSender().tell(new Info("Brak funduszy.", "Table"), getSelf()); 
            	}
            	else
            	{
            		String name = ((Call)message).getName(); 	
	        		pula = pula + stawka-moja_pula;
	        		getSender().tell(new Cash(nr,name,kasa,wpisowe), defaultTable); 
	        		notifyAll(new Info("Gracz "+name+" wyrównuje stawkę", "Table"));
	        		
	        		next();
            	}
        	}
        }
        else if(message instanceof Fold)
        {
        	int nr = ((Fold) message).getPlayerNumber();
        	if(nr!=licznik_wejsc || pierwsza_stawka || !wymiana_kart  || !rozdane_karty)
        		getSender().tell(new Info("Opcja zablokowana! (Sprawdz czy jest twoja kolej)", "Table"), getSelf());  
        	else
        	{
        		game.setFold(nr);
        		game.creator.hand[nr].remove_cards();
        		String name = ((Fold)message).getName(); 
        		notifyAll(new Info("Gracz "+name+" rezygnuje z dalszej gry", "Table"));
        		
        		next();
        	}
        }
        else if(message instanceof Allin)
        {
        	int nr = ((Allin) message).getPlayerNumber();
    		int moja_pula = game.pobierzPuleGracza(nr);
        	if(nr!=licznik_wejsc || pierwsza_stawka || !wymiana_kart  || !rozdane_karty)
        		getSender().tell(new Info("Opcja zablokowana! (Sprawdz czy jest twoja kolej lub czy została wstawiona pierwsza stawka)", "Table"), getSelf());  
        	else
        	{
        		all_in=true;
	        	int kasa = game.getCoins(nr); 
            	game.takeCoins(kasa,nr);   
            	game.ustawPuleGracza(nr, kasa);
            	if(kasa <= 0)
            	{
            		game.giveCoins(kasa, nr);
            		getSender().tell(new Info("Brak funduszy", "Table"), getSelf()); 
            	}
            	else 
            	{
            		
            		String name = ((Allin)message).getName(); 
            		if(stawka<kasa+moja_pula){
                		obejscie_stołu=0;
                		stawka = kasa+moja_pula;
            		}
                		pula = pula + kasa;
	        		kasa = game.getCoins(nr); 
	        		getSender().tell(new Cash(nr,name,kasa,wpisowe), defaultTable); 
	        		notifyAll(new Info("Gracz "+name+" wchodzi z całym życiowym dorobkiem(All-in), stawka wynosi : "+stawka, "Table"));
	        		
	        		next();	        		
            	}
        	}
        }
        else if (message instanceof Number)
        {   
        	
        	int nr = ((Number) message).getPlayerNumber();
        	if(nr!=licznik_wejsc || wymiana_kart)
        		getSender().tell(new Info("Prosze czekać na swoją kolej do wymiany kart", "Table"), getSelf());  
        	else
        	{
        		String kod = ((Number) message).getCode();
        		String[] zmienne=kod.split("");
        		
    	    	for(int j=2;j<=kod.length();j=j+2){
    	    		int sprawdzenie=Integer.parseInt(zmienne[j])-1;
    	    		if(sprawdzenie>=0)
    	    			game.creator.hand[nr].cards[sprawdzenie]=game.creator.deck.GetCard();
    	    		
    	    	}
	        	game.creator.hand[nr].sort();
	        	getSender().tell(new Info("Nowe karty:", "Table"), getSelf());    
	        	for(int j=0; j<5; j++)
	        		getSender().tell(new Card(game.getRankNr(nr,j), game.getColorNr(nr,j)), getSelf());     		
	        	
	        	next();
        	}
        }
        else if(message instanceof Win)  
        {       
        	String text ="";	
        	String name = ((Win)message).getName(); 
        	int nr = ((Win)message).getNumber();                
            if(nr==nr_zwyciezcy){
            	notifyAll(new Info("Wygrywa gracz "+name+" pulę "+pula+ " z kartami:","Table"));
            	game.giveCoins(pula, nr);
            	kasa = game.getCoins(nr); 
        		getSender().tell(new Cash(nr,name,kasa,wpisowe), defaultTable);
            	for(int j=0; j<5; j++){
            		text=game.creator.hand[nr_zwyciezcy].cards[j].getRank()+"	"+game.creator.hand[nr_zwyciezcy].cards[j].getColor();
            		notifyAll(new Info(text, name));
            	}
            }
        }         
    }
    
    public void next()  
    {
    	int liczbafoldow = game.getaNumberOfFolds();
    	if(liczbafoldow>=members.size()-1)
    	{
			notifyAll(new Info("Wyłaniamy zwycięzce", "Table"));		
			wymiana_kart=!wymiana_kart;
			rozdane_karty=!rozdane_karty;
			nr_zwyciezcy=game.Winner();
    		notifyAll(new Number(0,"",""));
		}
    	else
    	do
		{
    		licznik_wejsc++;
    		obejscie_stołu++;
    		if(licznik_wejsc>=members.size())
    			licznik_wejsc=0;
    		if(obejscie_stołu>=members.size()){
    			obejscie_stołu=0;
    			if(wymiana_kart && !wylaniamy_winnera)
    			{
	    			notifyAll(new Info("Wymieniamy karty", "Table"));		
	    			wymiana_kart=!wymiana_kart;
	    			notifyAll(new Change(licznik_wejsc));
    			}
    			else
    			{
    				if(wylaniamy_winnera || all_in)
    				{
	    				notifyAll(new Info("Wyłaniamy zwycięzce", "Table"));		
		    			wymiana_kart=!wymiana_kart;
		    			rozdane_karty=!rozdane_karty;
		    			nr_zwyciezcy=game.Winner();
	            		notifyAll(new Number(0,"",""));
    				}
    				else
    				{
    					wylaniamy_winnera=!wylaniamy_winnera;
    					notifyAll(new Info("Druga runda licytacji", "Table"));	
    					notifyAll(new Init(licznik_wejsc,"Table"));
    	    			wymiana_kart=!wymiana_kart;
    					
    				}
    			}
    		}
    		else
    		{
    			if(!wymiana_kart)
    				notifyAll(new Change(licznik_wejsc));
    			else
    				notifyAll(new Init(licznik_wejsc,"Table"));
    		}
    			
		}
    	while(game.checkFold(licznik_wejsc) && obejscie_stołu!=0);
    }
    
	static public void notifyAll(Object msg)
    {    	
        for (ActorRef member : members.values())
        {
            member.tell(msg, defaultTable);
        }
    }
	
	public static int getPula()
	{
		return pula;
	}
	
	public static int getStawka()
	{
		return stawka;
	}
	
	public static int getMyPula(int nr)
	{
		return game.pobierzPuleGracza(nr);
	}
    
    static public void Kick()
    {  
    	int i=0,kasa=0;
    	for (ActorRef member : members.values()) 
    	{
			kasa = game.getCoins(i);
			if(kasa<0)
			{
				member.tell(new Quit("table", i), defaultTable);
			}
            i++;
		}
    }
    
}
